/*
 * Copyright (c) 2005, 2014 vacoor
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 */
package org.vacoor.nothing.common.misc.util;

import java.lang.management.ManagementFactory;

/**
 * @author vacoor
 */
public abstract class Runtimes {
    private static final int pid;
    private static final CallStackHelper callStack = new CallStackHelper();

    static {
        int processId = -1;
        String name = ManagementFactory.getRuntimeMXBean().getName();
        try {
            processId = Integer.parseInt(name.substring(0, name.indexOf('@')));
        } catch (Exception ignore) {
        }
        pid = processId;
    }

    /**
     * 获取当前 JVM 的 PID
     * 当获取失败, 返回 -1
     * <p/>
     * {@link sun.management.RuntimeImpl#getName()} 调用 {@link sun.management.VMManagementImpl#getVmId()}
     * 主要代码如下
     * <p/>
     * <pre>
     * public String getVmId() {
     *    int i = getProcessId();
     *    String str = "localhost";
     *    try {
     *       str = InetAddress.getLocalHost().getHostName();
     *    } catch (UnknownHostException localUnknownHostException) { }
     *    return i + "@" + str;
     * }
     *
     * private native int getProcessId();
     * </pre>
     * <p/>
     *
     * @return
     */
    public static int getPID() {
        return pid;
    }


    /**
     * 获取当前位置的 StackTraceElement
     *
     * @return
     */
    public static StackTraceElement currentStackTrace() {
        StackTraceElement[] stackTrace = new Throwable().getStackTrace();
        return new Throwable().getStackTrace()[stackTrace.length > 1 ? 1 : 0];
    }

    /**
     * 获取当前位置的方法签名 eg:  org.vacoor.Test.main(Test.java:89)
     *
     * @return
     */
    public static String currentMethod() {
        StackTraceElement[] stackTrace = new Throwable().getStackTrace();
        return new Throwable().getStackTrace()[stackTrace.length > 1 ? 1 : 0].toString();
    }

    /**
     * 获取调用者 Class, 如果不存在则返回 null
     * 也可以使用 {@link sun.reflect.Reflection#getCallerClass(int)}, 但是这个是私有API还是不使用的好
     *
     * @param depth 调用栈相对深度, 如果在类A中调用该方法, 0 则返回 {@link Runtimes}, 1 则返回类A, 每一次调用(不论是相同类还是不同类都应该+1)
     * @return
     */
    public static Class<?> getCallerClass(int depth) {
        if (depth < 0) {
            return null;
        }
        return callStack.getCallStackClass(depth);

        /*
        // 先从调用上下文获取, 失败则从 StackTrace 中获取
        Class<?> caller = callStack.getCallStackClass(depth);
        // caller = null;
        if (caller != null) {
            return caller;
        }

        // 下面方法不可靠, 如果在同一个类中一个方法调用另外一个方法则该类会产生两个StackTraceElement
        StackTraceElement[] stackTrace = new Throwable().getStackTrace();
        for (int i = 0; i < stackTrace.length; i++) {
            System.out.println("-- " + i + " --:" + stackTrace[i].getClassName());
        }
        if (stackTrace.length >= 6 && depth > stackTrace.length - 6) { //-- 不一定
            // 跳过以下 StackTranceElement, 这些在 idea 中debug 不存在, run 时存在, 奇怪??
            // sun.reflect.NativeMethodAccessorImpl             -5
            // sun.reflect.NativeMethodAccessorImpl             -4
            // sun.reflect.DelegatingMethodAccessorImpl         -3
            // java.lang.reflect.Method                         -2
            // main                                             -1
            depth += 4;
        }
        if (depth < 0 || depth > stackTrace.length - 1) {
            return null;
        }

        String clazz = stackTrace[depth].getClassName();
        try {
            return Thread.currentThread().getContextClassLoader().loadClass(clazz);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
        */
    }

    private static class CallStackHelper extends SecurityManager {
        /**
         * 获取调用栈中指定深度(相对于调用该方法的类) 的 Class 对象, 如果不存在则返回 null
         */
        public Class<?> getCallStackClass(int callStackDepth) {
            Class[] context = getClassContext();
            if (-1 < callStackDepth && callStackDepth < context.length - 1) {
                return context[callStackDepth + 1]; // 去除栈顶的当前class
            }
            return null;
        }
    }

    private Runtimes() {
    }
}
